﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Nvigorate.Common;
using Nvigorate.Common.Reflection;
using Nvigorate.Relational.Record;

namespace Nvigorate.Relational.Mapping.Interface
{
    public interface IDiscriminate
    {
        Type GetAppropriateType(IRecord record);
        Type GetAppropriateType(IDataReader record);
    }

    [Serializable]
    public class ColumnPresenceDiscriminator : IDiscriminate
    {
        private Type _defaultType = typeof (object);
        private List<Tuple<Type, string>> _typeRequirements = new List<Tuple<Type, string>>();

        public Type GetAppropriateType(IRecord record)
        {
            var tuple = _typeRequirements
                .FirstOrDefault(r => record.FieldNames.Contains(r.Value2));
            return tuple == null ? _defaultType : tuple.Value1;
        }

        public Type GetAppropriateType(IDataReader record)
        {
            var tuple = _typeRequirements
                .FirstOrDefault(r => record.GetOrdinal(r.Value2) >= 0);
            return tuple == null ? _defaultType : tuple.Value1;
        }

        public ColumnPresenceDiscriminator RegisterType<T>(string column, string table)
        {
            _typeRequirements.Add(Tuple.Create(typeof(T), string.Format("{0}.{1}", table, column)));
            return this;
        }

        public ColumnPresenceDiscriminator RegisterType<TType, TColumn, TTable>(Expression<Func<TTable, TColumn>> column)
        {
            var expression = column.Body as MemberExpression;
            return RegisterType<TType>(expression.Member.Name, Reflector.Read(typeof(TTable), "TableName").ToString());
        }

        public ColumnPresenceDiscriminator(Type defaultType)
        {
            _defaultType = defaultType;
        }
    }

    [Serializable]
    public class ColumnValueDiscriminator : IDiscriminate
    {
        private Type _defaultType = typeof(object);
        private string _defaultTable = "";
        private string _defaultColumn = "";


        private List<Tuple<Type, string, object>> _typeRequirements = new List<Tuple<Type, string, object>>();

        public Type GetAppropriateType(IRecord record)
        {
            var tuple = _typeRequirements
                .Where(r => record.FieldNames.Contains(r.Value2))
                .FirstOrDefault(r => record[r.Value2].Equals(r.Value3));
            if(tuple == null)
            {
                return _defaultType;
            }
            else
            {
                return tuple.Value1;
            }
        }

        public Type GetAppropriateType(IDataReader record)
        {
            var tuple = _typeRequirements
                .Where(r => record.GetOrdinal(r.Value2) >= 0)
                .FirstOrDefault(r => record[r.Value2].Equals(r.Value3));
            if (tuple == null)
            {
                return _defaultType;
            }
            else
            {
                return tuple.Value1;
            }
        }

        public ColumnValueDiscriminator RegisterType<T>(string column, string table, object value)
        {
            _typeRequirements.Add(Tuple.Create(typeof(T), string.Format("{0}.{1}", table, column), value));
            return this;
        }

        public ColumnValueDiscriminator RegisterType<TType, TColumn, TTable>(Expression<Func<TTable, TColumn>> column, object value)
        {
            var expression = column.Body as MemberExpression;
            return RegisterType<TType>(expression.Member.Name, Reflector.Read(typeof (TTable), "TableName").ToString(), value);
        }

        public ColumnValueDiscriminator RegisterType<T>(object value)
        {
            return RegisterType<T>(_defaultColumn, _defaultTable, value);
        }

        public ColumnValueDiscriminator(Type defaultType)
        {
            _defaultType = defaultType;
        }

        public ColumnValueDiscriminator(Type defaultType, string defaultTable, string defaultColumn)
        {
            _defaultType = defaultType;
            _defaultTable = defaultTable;
            _defaultColumn = defaultColumn;
        }
    }

    [Serializable]
    public class PredicateDiscriminator : IDiscriminate
    {
        private List<Tuple<Func<IRecord, bool>, Type>> _predicates = new List<Tuple<Func<IRecord, bool>, Type>>();
        private Type _defaultType = typeof (object);

        public PredicateDiscriminator RegisterType<T>(Func<IRecord, bool> predicate)
        {
            _predicates.Add(Tuple.Create(predicate, typeof (T)));
            return this;
        }

        public Type GetAppropriateType(IRecord record)
        {
            Tuple<Func<IRecord, bool>, Type> tuple = _predicates.FirstOrDefault(p => p.Value1(record));
            return tuple == null ? _defaultType : tuple.Value2;
        }

        public Type GetAppropriateType(IDataReader record)
        {
            return typeof (object);
        }
    }
}
